package net.homelinux.chaoswg.io.renga.krypto;

import net.homelinux.chaoswg.io.datatypes.Pair;
import net.homelinux.chaoswg.io.datatypes.Unit;
import net.homelinux.chaoswg.io.higherorderfunctions.AbstractConstantFunction;
import net.homelinux.chaoswg.io.higherorderfunctions.NAryFunction;
import net.homelinux.chaoswg.io.renga.core.Initializer;
import net.homelinux.chaoswg.io.renga.core.animations.Animation;
import net.homelinux.chaoswg.io.renga.core.converter.IdentityConverter;
import net.homelinux.chaoswg.io.renga.core.gui.operations.Control;
import net.homelinux.chaoswg.io.renga.core.gui.operations.FunctionInputOperation;
import net.homelinux.chaoswg.io.renga.core.gui.operations.FunctionOutputOperation;
import net.homelinux.chaoswg.io.renga.core.gui.operations.Operation;
import net.homelinux.chaoswg.io.renga.core.gui.operations.PrimitiveOperation;
import net.homelinux.chaoswg.io.renga.core.gui.operations.factories.PrimitiveOperationFactory;
import net.homelinux.chaoswg.io.renga.core.gui.operations.visualisations.ImageVisualisation;
import net.homelinux.chaoswg.io.renga.core.gui.operations.visualisations.NamedBlockVisualisation;
import net.homelinux.chaoswg.io.renga.core.gui.operations.visualisations.SplitBlockVisualisation;
import net.homelinux.chaoswg.io.renga.core.gui.operations.visualisations.Visualisation;
import net.homelinux.chaoswg.io.renga.core.registries.ConverterRegistry;
import net.homelinux.chaoswg.io.renga.core.registries.OperationRegistry;
import net.homelinux.chaoswg.io.renga.core.registries.ReaderRegistry;
import net.homelinux.chaoswg.io.renga.krypto.animations.Add.AddAnimation;
import net.homelinux.chaoswg.io.renga.krypto.animations.Merge2.Merge2Animation;
import net.homelinux.chaoswg.io.renga.krypto.animations.Merge4.Merge4Animation;
import net.homelinux.chaoswg.io.renga.krypto.animations.Merge8.Merge8Animation;
import net.homelinux.chaoswg.io.renga.krypto.animations.Mult.MultAnimation;
import net.homelinux.chaoswg.io.renga.krypto.animations.Pht.PhtAnimation;
import net.homelinux.chaoswg.io.renga.krypto.animations.Rotate.LeftRotateAnimation;
import net.homelinux.chaoswg.io.renga.krypto.animations.Rotate.RightRotateAnimation;
import net.homelinux.chaoswg.io.renga.krypto.animations.Shift.LeftShiftAnimation;
import net.homelinux.chaoswg.io.renga.krypto.animations.Shift.RightShiftAnimation;
import net.homelinux.chaoswg.io.renga.krypto.animations.Split2.Split2Animation;
import net.homelinux.chaoswg.io.renga.krypto.animations.Split4.Split4Animation;
import net.homelinux.chaoswg.io.renga.krypto.animations.Split8.Split8Animation;
import net.homelinux.chaoswg.io.renga.krypto.animations.Subtract.SubtractAnimation;
import net.homelinux.chaoswg.io.renga.krypto.animations.Xor.XorAnimation;
import net.homelinux.chaoswg.io.renga.krypto.converter.BVtoUNBConverter;
import net.homelinux.chaoswg.io.renga.krypto.converter.StringToBVConverter;
import net.homelinux.chaoswg.io.renga.krypto.converter.StringToUNBConverter;
import net.homelinux.chaoswg.io.renga.krypto.converter.UNBtoBVConverter;
import net.homelinux.chaoswg.io.renga.krypto.datatypes.numerical.Unsigned128Bit;
import net.homelinux.chaoswg.io.renga.krypto.datatypes.numerical.Unsigned16Bit;
import net.homelinux.chaoswg.io.renga.krypto.datatypes.numerical.Unsigned32Bit;
import net.homelinux.chaoswg.io.renga.krypto.datatypes.numerical.Unsigned64Bit;
import net.homelinux.chaoswg.io.renga.krypto.datatypes.numerical.Unsigned8Bit;
import net.homelinux.chaoswg.io.renga.krypto.datatypes.numerical.UnsignedNBit;
import net.homelinux.chaoswg.io.renga.krypto.datatypes.numerical.UnsignedNBitFactory;
import net.homelinux.chaoswg.io.renga.krypto.datatypes.vectors.BitVector;
import net.homelinux.chaoswg.io.renga.krypto.gui.operations.LoopOperation;
import net.homelinux.chaoswg.io.renga.krypto.gui.operations.reader.LoopOperationReader;
import net.homelinux.chaoswg.io.renga.krypto.operations.misc.Select;
import net.homelinux.chaoswg.io.renga.krypto.operations.numerical.Add;
import net.homelinux.chaoswg.io.renga.krypto.operations.numerical.Mult;
import net.homelinux.chaoswg.io.renga.krypto.operations.numerical.Pht;
import net.homelinux.chaoswg.io.renga.krypto.operations.numerical.Sub;
import net.homelinux.chaoswg.io.renga.krypto.operations.type.Merge2;
import net.homelinux.chaoswg.io.renga.krypto.operations.type.Merge4;
import net.homelinux.chaoswg.io.renga.krypto.operations.type.Merge8;
import net.homelinux.chaoswg.io.renga.krypto.operations.type.PiI;
import net.homelinux.chaoswg.io.renga.krypto.operations.type.Split2;
import net.homelinux.chaoswg.io.renga.krypto.operations.type.Split4;
import net.homelinux.chaoswg.io.renga.krypto.operations.type.Split8;
import net.homelinux.chaoswg.io.renga.krypto.operations.vector.LeftShift;
import net.homelinux.chaoswg.io.renga.krypto.operations.vector.RightShift;
import net.homelinux.chaoswg.io.renga.krypto.operations.vector.Rol;
import net.homelinux.chaoswg.io.renga.krypto.operations.vector.Ror;
import net.homelinux.chaoswg.io.renga.krypto.operations.vector.Xor;

public class KryptoInitializer extends AbstractConstantFunction<Unit> implements Initializer {

    public Unit apply() {
        UnsignedNBitFactory unbf = UnsignedNBitFactory.createUnsignedNBitFactory();

        unbf.set(8, Unsigned8Bit.class);
        unbf.set(16, Unsigned16Bit.class);
        unbf.set(32, Unsigned32Bit.class);
        unbf.set(64, Unsigned64Bit.class);
        unbf.set(128, Unsigned128Bit.class);
        ConverterRegistry cregistry  = ConverterRegistry.getConverterRegistry();

        cregistry.setConverter(new Pair<Class, Class> (String.class, UnsignedNBit.class), new StringToUNBConverter ());
        cregistry.setConverter(new Pair<Class, Class> (String.class, BitVector.class), new StringToBVConverter());

        cregistry.setConverter(new Pair<Class, Class> (UnsignedNBit.class, BitVector.class), new UNBtoBVConverter ());
        cregistry.setConverter(new Pair<Class, Class> (Unsigned8Bit.class, BitVector.class), new UNBtoBVConverter ());
        cregistry.setConverter(new Pair<Class, Class> (Unsigned16Bit.class, BitVector.class), new UNBtoBVConverter ());
        cregistry.setConverter(new Pair<Class, Class> (Unsigned32Bit.class, BitVector.class), new UNBtoBVConverter ());
        cregistry.setConverter(new Pair<Class, Class> (Unsigned64Bit.class, BitVector.class), new UNBtoBVConverter ());
        cregistry.setConverter(new Pair<Class, Class> (Unsigned128Bit.class, BitVector.class), new UNBtoBVConverter ());

        cregistry.setConverter(new Pair<Class, Class> (BitVector.class, UnsignedNBit.class), new BVtoUNBConverter());
        cregistry.setConverter(new Pair<Class, Class> (BitVector.class, Unsigned8Bit.class), new BVtoUNBConverter());
        cregistry.setConverter(new Pair<Class, Class> (BitVector.class, Unsigned16Bit.class), new BVtoUNBConverter());
        cregistry.setConverter(new Pair<Class, Class> (BitVector.class, Unsigned32Bit.class), new BVtoUNBConverter());
        cregistry.setConverter(new Pair<Class, Class> (BitVector.class, Unsigned64Bit.class), new BVtoUNBConverter());
        cregistry.setConverter(new Pair<Class, Class> (BitVector.class, Unsigned128Bit.class), new BVtoUNBConverter());

        cregistry.setConverter(new Pair<Class, Class>(UnsignedNBit.class, UnsignedNBit.class), new IdentityConverter<UnsignedNBit> ());
        cregistry.setConverter(new Pair<Class, Class>(UnsignedNBit.class, Unsigned8Bit.class), new IdentityConverter<UnsignedNBit> ());
        cregistry.setConverter(new Pair<Class, Class>(UnsignedNBit.class, Unsigned16Bit.class), new IdentityConverter<UnsignedNBit> ());
        cregistry.setConverter(new Pair<Class, Class>(UnsignedNBit.class, Unsigned32Bit.class), new IdentityConverter<UnsignedNBit> ());
        cregistry.setConverter(new Pair<Class, Class>(UnsignedNBit.class, Unsigned64Bit.class), new IdentityConverter<UnsignedNBit> ());
        cregistry.setConverter(new Pair<Class, Class>(UnsignedNBit.class, Unsigned128Bit.class), new IdentityConverter<UnsignedNBit> ());

        cregistry.setConverter(new Pair<Class, Class>(Unsigned8Bit.class, UnsignedNBit.class), new IdentityConverter<UnsignedNBit> ());
        cregistry.setConverter(new Pair<Class, Class>(Unsigned16Bit.class, UnsignedNBit.class), new IdentityConverter<UnsignedNBit> ());
        cregistry.setConverter(new Pair<Class, Class>(Unsigned32Bit.class, UnsignedNBit.class), new IdentityConverter<UnsignedNBit> ());
        cregistry.setConverter(new Pair<Class, Class>(Unsigned64Bit.class, UnsignedNBit.class), new IdentityConverter<UnsignedNBit> ());
        cregistry.setConverter(new Pair<Class, Class>(Unsigned128Bit.class, UnsignedNBit.class), new IdentityConverter<UnsignedNBit> ());

        cregistry.setConverter(new Pair<Class, Class>(BitVector.class, BitVector.class), new IdentityConverter<BitVector> ());

        /*--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
        
        ReaderRegistry orr = ReaderRegistry.getOperationRegistry();
        orr.setOperationReader(LoopOperation.class, new LoopOperationReader ());

        
        OperationRegistry.getOperationRegistry().setOperationFactory("Add", 
                new PrimitiveOperationFactory ().curry (new AbstractConstantFunction<NAryFunction> () { public NAryFunction apply () {return new Add (); } },
                        new AbstractConstantFunction<Animation> () { public Animation apply () { return new AddAnimation(); } },
                        new AbstractConstantFunction<Visualisation>() { public Visualisation apply () { return new ImageVisualisation("Add.png"); } }));

        OperationRegistry.getOperationRegistry().setOperationFactory("Xor", new AbstractConstantFunction<Operation> () {
            public Operation apply () {
                return new PrimitiveOperation (new Xor (), new XorAnimation(), new ImageVisualisation("Xor.png"));
            }
        });

        OperationRegistry.getOperationRegistry().setOperationFactory("Pht", new AbstractConstantFunction<Operation> () {
            public Operation apply () {
                return new PrimitiveOperation (new Pht (), new PhtAnimation (), new ImageVisualisation("PHT.png"));
            }
        });

        OperationRegistry.getOperationRegistry().setOperationFactory("Subtract", new AbstractConstantFunction<Operation> () {
            public Operation apply () {
                return new PrimitiveOperation (new Sub (), new SubtractAnimation (), new ImageVisualisation("Subtract.png"));
            }
        });

        OperationRegistry.getOperationRegistry().setOperationFactory("Multiply", new AbstractConstantFunction<Operation> () {
            public Operation apply () {
                return new PrimitiveOperation (new Mult (), new MultAnimation(), new ImageVisualisation("Mult.png"));
            }
        });

//      OperationRegistry.getOperationRegistry().setOperationFactory("Exp", new AbstractConstantFunction<Operation> () {
//      public Operation apply () {
//      return new PrimitiveOperation (new Exp(), null, new ImageVisualisation("PHT.png"));
//      }
//      });

        OperationRegistry.getOperationRegistry().setOperationFactory("Split 2", new AbstractConstantFunction<Operation> () {
            public Operation apply () {
                return new PrimitiveOperation (new Split2(), new Split2Animation (), new SplitBlockVisualisation (0,0, 128, 32,2));
            }
        });

        OperationRegistry.getOperationRegistry().setOperationFactory("Merge 2", new AbstractConstantFunction<Operation> () {
            public Operation apply () {
                return new PrimitiveOperation (new Merge2(), new Merge2Animation (), new SplitBlockVisualisation(0,0, 128, 32,2));
            }
        });

        OperationRegistry.getOperationRegistry().setOperationFactory("Split 4", new AbstractConstantFunction<Operation> () {
            public Operation apply () {
                return new PrimitiveOperation (new Split4(), new Split4Animation (), new SplitBlockVisualisation (0,0, 128, 32,4));
            }
        });

        OperationRegistry.getOperationRegistry().setOperationFactory("Merge 4", new AbstractConstantFunction<Operation> () {
            public Operation apply () {
                return new PrimitiveOperation (new Merge4(), new Merge4Animation(), new SplitBlockVisualisation(0,0, 128, 32,4));
            }
        });

        OperationRegistry.getOperationRegistry().setOperationFactory("Split 8", new AbstractConstantFunction<Operation> () {
            public Operation apply () {
                return new PrimitiveOperation (new Split8(), new Split8Animation (), new SplitBlockVisualisation (0,0, 128, 32,8));
            }
        });

        OperationRegistry.getOperationRegistry().setOperationFactory("Merge 8", new AbstractConstantFunction<Operation> () {
            public Operation apply () {
                return new PrimitiveOperation (new Merge8(), new Merge8Animation (), new SplitBlockVisualisation(0,0, 128, 32,8));
            }
        });

        OperationRegistry.getOperationRegistry().setOperationFactory("Rotate Left", new AbstractConstantFunction<Operation> () {
            public Operation apply () {
                return new PrimitiveOperation (new Rol (), new LeftRotateAnimation(), new ImageVisualisation ("Rol_1.png"));
            }
        });

        OperationRegistry.getOperationRegistry().setOperationFactory("Rotate Right", new AbstractConstantFunction<Operation> () {
            public Operation apply () {
                return new PrimitiveOperation (new Ror (), new RightRotateAnimation (), new ImageVisualisation ("Ror_1.png"));
            }
        });

        OperationRegistry.getOperationRegistry().setOperationFactory("Left Shift", new AbstractConstantFunction<Operation> () {
            public Operation apply () {
                return new PrimitiveOperation (new LeftShift (), new LeftShiftAnimation(), new ImageVisualisation ("ShiftL_1.png"));
            }
        });

        OperationRegistry.getOperationRegistry().setOperationFactory("Right Shift", new AbstractConstantFunction<Operation> () {
            public Operation apply () {
                return new PrimitiveOperation (new RightShift (), new RightShiftAnimation(), new ImageVisualisation ("ShiftR_1.png"));
            }
        });

        OperationRegistry.getOperationRegistry().setOperationFactory("Select", new AbstractConstantFunction<Operation> () {
            public Operation apply () {
                return new PrimitiveOperation(new Select (), null, new NamedBlockVisualisation ("Select")); 
            }
        });

        OperationRegistry.getOperationRegistry().setOperationFactory("Pi i", new AbstractConstantFunction<Operation> () {
            public Operation apply () {
                return new PrimitiveOperation(new PiI (), null, new NamedBlockVisualisation (0,0,32,32,"Pi i")); 
            }
        });

        OperationRegistry.getOperationRegistry().setOperationFactory("Loop Operation", new AbstractConstantFunction<Operation> () {
            public Operation apply () {
                final Control ctrl = new Control ();
                LoopOperation loop = new LoopOperation (ctrl);
                ctrl.addOperation(new FunctionInputOperation (loop,0), false);
                ctrl.addOperation(new FunctionInputOperation (loop,1), false);
                ctrl.addOperation(new FunctionOutputOperation(loop), false);

                return loop;
            }
        });
        
        return null;
    }

}
